using System;
using System.Collections;
using System.Collections.Generic;
using System.Html;
using System.Runtime.CompilerServices;
using Awaiter;
using Nsb.Classes;
using ThemeChain;
using WhirliGig.Classes;
using jQueryApi;

namespace PlayNexus.Classes
{
    public class CloudWidget
    {
        private readonly List<GigData> _items = new List<GigData>();

        private Dictionary<string, object> _data;
        private int _feedIndex;
        private Dictionary<string, jQueryObject> _grouped; // Used to group elements into a node while building nodes
        private jQueryObject _host;
        private jQueryObject _hostInner;
        private string _id;
        private string _key;
        private int _mg = 10;
        private int _moveBy;
        private Dictionary _nodeCss;
        private int _panels;
        private string _partitionKey = "db/feeds";
        private string _rowKey = "home";
        private Dictionary _tmplCss;
        private jQueryObject _win;
        private jQueryObject _view;

        private jQueryObject NextFeedItem
        {
            get
            {
                try
                {
                    return GigShow.GigElById(_items[_feedIndex++].id);
                }
                catch
                {
                    return null;
                }
            }
        }

        private jQueryObject PrevFeedItem
        {
            get
            {
                try
                {
                    return GigShow.GigElById(_items[--_feedIndex].id);
                }
                catch
                {
                    return null;
                }
            }
        }

        [PreserveName, PreserveCase]
        public static void Create(Gig gg, GigData gd)
        {
            CloudWidget cw = new CloudWidget();
            cw._key = cw.GetType().Name;
            gg.Element
                .Data(cw._key, cw)
                //.Click(cw.NextEv)
                ;
            cw.Init(gd);
        }

        private void Init(GigData gd)
        {
            _id = gd.id;
            _data = gd.data ?? new Dictionary<string, object>();
            _hostInner = GigShow.FindInGroup(gd.groupId, ".Slider");
            _panels = Script.Value((int) _data["panels"], 4);
            _hostInner.Empty();

            CollectPanels();

            _host = _hostInner.Closest(Gig.DotGigClassName);
            _host
                .CSS(new Dictionary("border", "0px none", "margin", 0, "padding", 0, "display", "table"))
                //.Data("doubleclick", (jQueryEventHandler) EditEv)
                .AddClass("SliderHostEl")
                ;

            _hostInner
                .MouseOver(delegate(jQueryEvent e) { jQuery.FromElement(e.Target).AddClass("Hover"); })
                .MouseOut(delegate(jQueryEvent e) { jQuery.FromElement(e.Target).RemoveClass("Hover"); })
                ;

            _tmplCss = (Dictionary) jQuery.Extend(new Dictionary(), GigShow.Instance.GigById(_host.GetAttribute("id")).css);
            int w = Number.ParseInt(((_host.GetInnerWidth() - (_mg*(_panels - 1)))/_panels)) - HorzPadding(_tmplCss);
            int h = Number.ParseInt(_host.GetInnerHeight() - VertPadding(_tmplCss));
            int h2 = h + VertPadding(_tmplCss);

            _moveBy = Number.ParseInt(w + HorzPadding(_tmplCss) + _mg);

            _nodeCss =
                new Dictionary(
                    "position", "relative",
                    "display", "inline-block",
                    "overflow", "hidden",
                    "vertical-align", "top",
                    "top", 0,
                    "width", w,
                    "height", h,
                    "margin-right", _mg
                    );

            _view = jQuery.FromHtml("<div/>")
                .AppendTo(_hostInner)
                .CSS(new Dictionary(
                         "position", "relative",
                         "top", 0,
                         "left", 0,
                         "width", Number.ParseInt(_host.GetWidth()),
                         "overflow", "hidden",
                         "height", h2,
                         "margin", 0,
                         "padding", 0
                         ))
                ;

            _win = jQuery.FromHtml("<div/>")
                .AppendTo(_view)
                .CSS(new Dictionary(
                         "position", "relative",
                         "top", 0,
                         "left", 0,
                         "width", _items.Count*w + w,
                         "height", h2,
                         "margin", 0,
                         "padding", 0
                         ))
                ;

            jQuery.FromHtml("<div/>")
                .AppendTo(_host)
                .AddClass("SliderSlider SliderLeft")
                .Click(PrevEv)
                .Html(ThemeBase.LeftMark)
                ;

            jQuery.FromHtml("<div/>")
                .AppendTo(_host)
                .AddClass("SliderSlider SliderRight")
                .Click(NextEv)
                .Html(ThemeBase.RightMark)
                ;

            RefreshNodesAw(Await.AsyncAw);
        }


        public void RefreshNodesAw(Await awp)
        {
            new Await()
                .AddFn(CollectPanels)
                .AddDx(
                    delegate
                    {
                        _feedIndex = 0;
                        _win.Empty();
                        _grouped = new Dictionary();
                        foreach (GigData gd in _items)
                        {
                            jQueryObject node;
                            if (gd.groupId != null)
                            {
                                node = _grouped[gd.groupId] ?? NewNode().AppendTo(_win).Empty();
                                _grouped[gd.groupId] = node;
                            }
                            else
                                node = NewNode().AppendTo(_win).Empty();
                            jQueryObject s = GigShow.PageSelectToEl.Find("#" + gd.id);
                            s.AppendTo(node).Show();
                            GigShowEdit.GigEditByEl(s).ScoopPosition();
                            if (_feedIndex < _panels)
                                _feedIndex++;
                        }
                    })
                .Commit(awp);
        }

        public void CollectPanels()
        {
            if (_items.Count > 0)
                return;
            foreach (KeyValuePair<string, GigData> p in GigShow.CurrentGigs)
            {
                GigData gd = p.Value;
                if (gd.parentId == _id)
                    _items.Add(gd);
            }
        }

        [PreserveName, PreserveCase]
        public static void PasteUpdateGigData(Gig gg, GigData gdParent)
        {
            GigData gd = GigShow.Instance.GigById(gg.Id);
            gd.parentId = gdParent.id;
            jQueryObject el = GigShow.FindInGroup(gdParent.groupId, ".SliderHostEl");
            gd.pos.Top = Number.ParseInt(el.GetCSS("padding-top"));
            gd.pos.Left = Number.ParseInt(el.GetCSS("padding-left"));
            gd.pos.Top = 0;
            gd.pos.Left = 0;
        }

        [PreserveName, PreserveCase]
        public static void Place(Gig gg, GigData gd)
        {
            gg.AppendTo(GigShow.PageSelectToEl).Element.Hide();
        }

        public jQueryObject NewNode()
        {
            object my = this;
            return
                jQuery.FromHtml("<div/>")
                    .AddClass("Node")
                    .CSS(_tmplCss)
                    .CSS(_nodeCss)
                    .Plugin<Jewl>().Droppable(
                        new Dictionary(
                            // Done this way so the native "this" can be passed to the event function as well as the C# this
                            // Native "this" represents the drop target
                            "drop", Script.Literal("function(a,b) {{{0}.DropEv(a,b,this);}}", my),
                            "over", Script.Literal("function(a,b) {{{0}.OverEv(a,b,this);}}", my),
                            "out", Script.Literal("function(a,b) {{{0}.OutEv(a,b,this);}}", my)
                            ))
                ;
        }

        private void GroupNode(jQueryObject node, Gig gg)
        {
            jQueryObject firstGig = node.Find(Gig.DotGigClassName);
            if (firstGig.Length > 0)
            {
                GigData fgd = GigShow.GigByEl(firstGig);
                string gid = fgd.groupId ?? Formulas.NewId;
                GigShowEdit.GroupIt(fgd.id, gid);
                GigShowEdit.GroupIt(gg.Id, gid);
            }
        }

        [PreserveName, PreserveCase]
        public void DropEv(jQueryEvent e, jQueryUiObject ui, Element target)
        {
            Inform.Debug("DropEv0");
            //Script.Literal("debugger");
            jQueryObject targetEl = jQuery.FromElement(target);
            //targetEl = (jQueryObject)targetEl.GetDataValue(DropTargetKey) ?? targetEl;

            // Ignore nodes outside the slider window
            jQueryPosition p1 = targetEl.GetOffset();
            jQueryPosition p2 = _view.GetOffset();
            if (p1.Left < p2.Left || p1.Left >= p2.Left + _view.GetWidth() || p1.Top >= p2.Top + _view.GetHeight())
                return;
            Inform.Debug("p1 top={0} left={1}, p2 top={2} left={3}", p1.Top, p1.Left, p2.Top, p2.Left);
            Inform.Debug("width={0} height={1}", _view.GetWidth(), _view.GetHeight());

            //Inform.Debug("DropEv1");
            Gig gg = GigEdit.GigGigByEl(ui.draggable);
            if (gg != null)
            {
                jQueryEventHandler fn = null;
                fn = delegate
                     {
                         //Inform.Debug("DropEv2");
                         PasteUpdateGigData(gg, GigShow.Instance.GigById(_id));
                         GroupNode(targetEl, gg);

                         // Convert gig data position relative to node
                         jQueryPosition pA = targetEl.GetOffset();
                         jQueryPosition pB = ui.draggable.GetOffset();
                         GigData gd = GigShow.Instance.GigById(gg.Id);
                         gd.pos.Top = pB.Top - pA.Top;
                         gd.pos.Left = pB.Left - pA.Left;
                         // GigDataOffFix
                         //gd.off.Top = pB.Top;
                         //gd.off.Left = pB.Left;
                         ui.draggable.AppendTo(targetEl);
                         ui.draggable.CSS(new Dictionary("top", gd.pos.Top, "left", gd.pos.Left));
                         jQuery.Document.Unbind(GigShowEdit.AfterDragName, fn);
                     }
                    ;

                jQuery.Document
                    .Bind(GigShowEdit.AfterDragName, fn);
            }
        }

        [PreserveName, PreserveCase]
        public void OverEv(jQueryEvent e, jQueryUiObject ui, Element target)
        {
            //Inform.Debug("OverEv0");
            jQueryObject dropEl = jQuery.FromElement(target);
            AddRubberband(dropEl);
        }

        [PreserveName, PreserveCase]
        public void OutEv(jQueryEvent e, jQueryUiObject ui, Element target)
        {
            //Inform.Debug("OutEv0");
            jQueryObject dropEl = jQuery.FromElement(target);
            RemRubberband(dropEl);
        }

        public void NextEv(jQueryEvent e)
        {
            if (_feedIndex + 1 >= _items.Count)
                return;
            _feedIndex++;
            _win.Animate(
                new Dictionary(
                    "margin-left", "-=" + _moveBy
                    ),
                new Dictionary(
                    "duration", 500
                    )
                );
        }

        public void PrevEv(jQueryEvent e)
        {
            if (_feedIndex <= _panels)
                return;
            _feedIndex--;
            _win.Animate(
                new Dictionary(
                    "margin-left", "+=" + _moveBy
                    ),
                new Dictionary(
                    "duration", 500
                    )
                );
        }

        private const string DropTargetKey = "DropTargetKey";

        public static void AddRubberband(jQueryObject el)
        {
            Pos p = (Pos)(object) el.GetOffset();
            jQueryObject band = jQuery.FromHtml("<div/>")
                .CSS(new Dictionary(
                    "position", "absolute",
                    "top", p.Top-1,
                    "left", p.Left-1,
                    "width", el.GetInnerWidth() + 2,
                    "height", el.GetInnerHeight() + 2,
                    "border", "1px solid red",
                    "background-color", "rgba(255,255,255,.25)"
                         ))
                .AddClass(GigShowEdit.MaskClassName)
                //.Data(DropTargetKey, el)
                ;
            el.Data("band", band);
            band.AppendTo(Document.Body);
        }

        public static void RemRubberband(jQueryObject el)
        {
            jQueryObject band = (jQueryObject) el.GetDataValue("band");
            if (band != null)
                band.Remove();
            el.RemoveData("band");
        }

        public static int HorzPadding(Dictionary<string, string> css)
        {
            int pl = Number.ParseInt(css["padding-left"] ?? css["padding"] ?? "0");
            int pr = Number.ParseInt(css["padding-right"] ?? css["padding"] ?? "0");
            if (css["border-style"] != null && css["border-style"] != "none")
                return
                    pl + pr
                    +
                    Number.ParseInt(css["border-left-width"] ?? css["border-width"] ?? "0")
                    +
                    Number.ParseInt(css["border-right-width"] ?? css["border-width"] ?? "0")
                    ;
            return pl + pr;
        }

        public static int VertPadding(Dictionary<string, string> css)
        {
            int pt = Number.ParseInt(css["padding-top"] ?? css["padding"] ?? "0");
            int pb = Number.ParseInt(css["padding-bottom"] ?? css["padding"] ?? "0");
            if (css["border-style"] != null && css["border-style"] != "none")
                return
                    pt + pb
                    +
                    Number.ParseInt(css["border-top-width"] ?? css["border-width"])
                    +
                    Number.ParseInt(css["border-bottom-width"] ?? css["border-width"])
                    ;
            return pt + pb;
        }

        /*
                public void EditEv(jQueryEvent e)
                {
                    TargetEvent te = DomElement.CalcTargets(e);
                    int panel = Number.ParseInt(te.TargetX*_panels);
                    Inform.Debug("PANEL {0}, TargetX {1}", panel, te.TargetX);

                    new Await()
                        .AddAw(new Ask().TextAreaAw, new Dictionary("Msg", "Change description", "Value", _hostInner.Find(".Node").Eq(panel).GetHtml()))
                        .AddAw(DomElement.RequireResultAw, DomElement.NotFailOrEmpty)
                        .AddDl(delegate(Await aw) { StoreItemAw(aw, panel, (string) aw.Result); })
                        .Commit();
                }


                public void WriteToFeedAw(Await awp, string category, string msg)
                {
                    Debug.Assert(category != null, "toId != null");
                    FeedItem fd = new FeedItem(category, msg);

                    new Await()
                        .AddAw(FileBlobBase.AddListItemAw, _partitionKey, _rowKey, fd)
                        .Commit(awp);
                }

                private void LoadAw(Await awp)
                {
                    new Await()
                        .AddAw(FileBlobBase.LoadListAw, _partitionKey, _rowKey)
                        .AddDx(delegate(Await aw) { Items = (List<FeedItem>) aw.Result; })
                        .Commit(awp);
                }

                public void StoreItemAw(Await awp, int panel, string msg)
                {
                    Debug.Assert(panel < Items.Count, "Storing beyond item index");
                    //Items[panel].message = msg;
                    new Await()
                        .AddDl(
                            delegate(Await aw)
                            {
                                FileBlobBase.UpdateListAw(aw, _partitionKey, _rowKey,
                                                          delegate(object lx)
                                                          {
                                                              List<FeedItem> ls = (List<FeedItem>) lx;
                                                              ls[panel].message = msg;
                                                              Items = ls;
                                                          });
                            })
                        .AddAw(RefreshNodesAw)
                        .Commit(awp);
                }
        */
    }
}